package main

import (
	"bufio"
	"flag"
	"io"
	"log"
	"os"
	"strconv"
	"strings"
	"text/template"
)

const tm = `package error

const (
	{{range $name, $code := .CodeNameMap }}{{$name}} = {{$code}}
	{{end}}
)

func GetMsg(code int) string {
	switch code { {{range $code, $message := .CodeMsgMap}}
	case {{$code}}:
		return "{{$message}}"{{end}}
	default:
		return "unknown"
	}
}
`

type Code struct {
	CodeMsgMap  map[int]string
	CodeNameMap map[string]int
}

type stringArray []string

func (s *stringArray) String() string { return strings.Join(*s, " ") }
func (s *stringArray) Set(v string) error {
	arrayV := strings.Split(v, ",")
	for _, vs := range arrayV {
		if vs != "" {
			*s = append(*s, vs)
		}
	}
	return nil
}

// examples
// go run error_generator.go --file ./demo.error --tag tag2
// go run error_generator.go --file ./demo.error --tag tag,tag2
func main() {
	filepath := flag.String("file", "./demo.error", "error file path")
	var tags stringArray
	flag.Var(&tags, "tag", "use tags")
	flag.Parse()

	code := parsefile(*filepath, tags)

	tmpl := template.New("tmpl1")
	tmpl.Parse(tm)
	tmpl.Execute(os.Stdout, code)
}

func parsefile(filepath string, tags []string) *Code {
	f, err := os.Open(filepath)
	if err != nil {
		panic(err)
	}
	defer f.Close()
	CodeMsgMap := make(map[int]string)
	CodeNameMap := make(map[string]int)

	br := bufio.NewReader(f)

	valid := false
	for {
		ss, _, c := br.ReadLine()
		if c == io.EOF {
			break
		}
		s := strings.Split(strings.TrimSpace(string(ss)), ":")
		lens := len(s)
		switch lens {
		case 0, 1:
			continue
		case 2:
			tagName := s[0]
			if !contains(tags, tagName) {
				valid = false
				continue
			}
			valid = true
		case 3:
			if !valid {
				continue
			}
			name := strings.TrimSpace(s[0])
			code, err := strconv.Atoi(strings.TrimSpace(s[1]))
			if err != nil {
				log.Fatalf("can not parse code: %s", string(ss))
			}
			msg := strings.TrimSpace(s[2])
			CodeMsgMap[code] = msg
			CodeNameMap[name] = code
		default:
			log.Fatalf("can not parse: %s", string(ss))
		}
	}
	return &Code{
		CodeMsgMap:  CodeMsgMap,
		CodeNameMap: CodeNameMap,
	}
}

func contains(s []string, e string) bool {
	for _, a := range s {
		if a == e {
			return true
		}
	}
	return false
}
